/*
 * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it would be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Further, this software is distributed without any warranty that it is
 * free of the rightful claim of any third person regarding infringement
 * or the like.  Any license provided herein, whether implied or
 * otherwise, applies only to this software file.  Patent licenses, if
 * any, provided herein do not apply to combinations of this program with
 * other software, or any other product whatsoever.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write the Free Software Foundation, Inc., 59
 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
 *
 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
 * Mountain View, CA  94043, or:
 *
 * http://www.sgi.com
 *
 * For further information regarding this notice, see:
 *
 * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
 *
 */
/* $Id: tag_report.c,v 1.2 2006/12/13 22:55:22 vapier Exp $ */
#include "tag_report.h"
#include "debug.h"
#include "reporter.h"
#include "splitstr.h"

static char * worst_case(char *, char *);

/************************************************************************
 *			Report Generation				*
 ************************************************************************/

/*
 * printf format statement for standard reports
 * 5 fields with max/min widths
 */
#define FORMAT "%-20.20s %-15.15s %10.10s %-20.20s %s\n"

/*
 *  This is the central results reporting function.  All standard report
 *  format results are printed thru test_result.
 */
int
test_result(tag, tcid, tc, result, tags)
    char *tag, *tcid, *tc;
    char *result;
    SYM tags;
{
    char *expert, expkey[KEYSIZE];
    register char *c;
    char **cont;
    const char **cont_save;

    if(tcid == NULL)
	tcid = "-";
    if(tc == NULL)
	tc = "-";
    if(tag == NULL)
	tag = "test_result: no tag";
    if(result == NULL)
	result = "(RESULT IS NULL)";

    strcpy(expkey, "contacts");
    /* note: the sym_get here does _not_ change the "cursor" */
    if((expert = (char *)sym_get(tags, expkey)) == NULL) {
	expert = "UNKNOWN";
    }

    /* ' tr " " "_" ' */
    for(c = result; *c; c++) {
	if(*c == ' ') {
	    *c = '_';
	}
    }
    if(*result == '\0')
	result = "?";

    /* split contacts on "," and print out a line for each */
    cont_save = splitstr(expert, ",", NULL);
    for(cont=(char **)cont_save;*cont != NULL; cont++) {
	    printf(FORMAT, tag, tcid, tc, result, *cont);
    }
    splitstr_free(cont_save);

    return 0;
}


/*
 * CUTS test reporting.
 *
 *  (1) make a list (2d char array) of all TCIDs (see above for why)
 *  (2) look thru the list:
 *	(a) keep track of the "worst case" in this *TAG*
 *	(b) report each testcase's results
 *	(c) if the testcase number is != 0, count it
 *  (3) report tag's results
 *  (4) check the number of expected results with the actual results,
 *	report an error if they don't match.
 */

int
cuts_report(tags, keys, at, tag )
    SYM tags, keys; char *at, *tag;
{
    DBT Key, Data;

    /* analysis type: count of CUTS test cases */
    const char **ant;
    char *dat;		/* strdup(at) */
    int tccount;	/* expected count of testcases */
    int tcnum;		/* seen count of testcases */

    /* a list of tcids */
    char **taglist, **tl;
    int ntags, tagcount;

    char key_get[255];

    char *result = "", *worst_case();		/* overall result */

    /* parse analysis type: cuts:tc-count */
    ant = splitstr((dat=strdup(at)), ":",NULL);
    if(ant[1] != NULL)
	tccount=atoi(ant[1]);
    else
	tccount=0;
    free(dat);
    splitstr_free(ant);

    /* extract tcids */
    ntags=NTCID_START;
    taglist= (char **)malloc(sizeof(char *) * ntags);
    tagcount=0;

    tl = taglist;
    sym_seq(tags, &Key, &Data, R_FIRST);
    do {
	if(tagcount == ntags) {
	    /* exceeded tag array size -- realloc */
	    ntags += NTCID_START;
	    taglist= (char **)realloc(taglist, sizeof(char *) * ntags);
	    tl = taglist+tagcount;
	}

	if(strcmp((char *)Key.data, "_keys")==0)
	    continue;
	DEBUG(D_REPORT, 10)
	    printf("cuts_report: tcid %s\n", (char *)Key.data);
	*tl++ = Key.data;
	tagcount++;
    } while(sym_seq(tags, &Key, &Data, R_NEXT)==0);

    if(tagcount == ntags) {
	/* exceeded tag array size -- realloc */
	ntags++;	/* need just one more */
	taglist= (char **)realloc(taglist, sizeof(char *) * ntags);
	tl = taglist+tagcount;
    }

    *tl++ = NULL;

    ntags = tagcount;

    /* dump all found records */
    tcnum=0;
    for(tl=taglist; *tl != NULL; tl++) {

	strcpy(key_get, *tl);
	Key.data = (void *) key_get;

	/*sym_dump_s(sym_get(tags, key_get), 0);*/

	sym_seq(tags, &Key, &Data, R_CURSOR);
	do {
	    DEBUG(D_REPORT, 10)
		printf("cuts_report: tc %s = %s\n", (char *)Key.data,
                        (char *)Data.data);
	    result = worst_case(result, (char *)Data.data);
	    test_result(tag, *tl, (char *)Key.data, (char *)Data.data, keys);
	    if(atoi((char *)Key.data))
		tcnum++;
	} while(sym_seq(tags, &Key, &Data, R_NEXT)==0);
    }

    test_result(tag, "*", "*", result, keys);

    if(tccount != 0 && tccount != tcnum)
	test_result(tag, "-", "-", "TC count wrong", keys);

    free(taglist);

    return 0;
}

/*
 * Do the report generation.
 *
 * A problem: I really need multiple cursors.  I'd rather not look into
 * the depths of the current symbol table implimentation (there are the
 * cursors there that I could use) so that a different (faster!) symbol
 * table can be used in the future.
 * 
 * I could get a key (tag), get it's sub-keys (TCIDs), then get the key
 * again to reset to the top level, _then_ get the next key.  That would
 * be very inefficient.
 * 
 * The solution I chose is to extract all tags into a list (char array),
 * then go thru that list with the cursor free for other levels to use.
 *
 *  (1) make a list (2d char array) of all Tags
 *  (2) search for the first tag that has a "stime" record, and use that as
 *      the date (MMDDYY) that the tests were run.
 *  (3) print the report header
 *  (4) go thru all tags and report each as described at the beginning of 
 *      this file
 */
int
tag_report(alltags, ctag, keys)
    SYM alltags, ctag, keys;
{
    
    extern int extended;
    
    char key_get[KEYSIZE];
    char *info;
    
    /* retrieved _keys values: initation status, start time, duration,
     * termination type, termination id, start line, end line.		*/
    char *tag, *contact, *is, *mystime, *duration, *tt, *ti, *sl, *el;
    
    /* Check all driver-level status first */
    strcpy(key_get, "tag");
    if((tag = (char *)sym_get(keys, key_get)) == NULL) {
	return -1;
    }
    
    /* Check all driver-level status first */
    strcpy(key_get, "initiation_status");
    if((is = (char *)sym_get(keys, key_get)) == NULL) {
	test_result(tag, NULL, NULL, "no init status", keys);
	return -1;
    }
    
    if(strcmp(is, "ok")) {
	test_result(tag, NULL, NULL, is, keys);
    } else {
	
	strcpy(key_get, "corefile");
	if((info = (char *)sym_get(keys, key_get)) != NULL)
	    if(strcmp(info, "no") != 0) {
		test_result(tag, NULL, NULL, "coredump", keys);
	    }
	
	strcpy(key_get, "termination_type");
	if((tt = (char *)sym_get(keys, key_get))==NULL) {
	    test_result(tag, NULL, NULL, "no Term Type", keys);
	    return -1;
	}
	
	if(strcmp(tt, "exited")) {
	    test_result(tag, NULL, NULL, tt, keys);
	}
	
	strcpy(key_get, "analysis");
	if((info = (char *)sym_get(keys, key_get)) == NULL) {
	    test_result(tag, NULL, NULL, "no Analysis Type", keys);
	    return -1;
	}
	
	/* Getting here indicates that there were no fatal driver-level
	 * errors.  Do the kind of reporting requested by the test.
	 */
	
	if(strncmp(info, "none", 4) == 0 ) {
            /*
             * If analysis is 'none', alway report the test as
             * a pass regardless of output or exit status.
             */
	    test_result(tag, NULL, NULL, "pass", keys);

	} else if(strncmp(info, "cuts", 4)) {

            /*
             * If analysis is not cuts, assume it is 'exit', thus
             * the termination_id is used to determine pass/fail result.
             */
	    if ( strcmp(tt, "timeout") ) {
	        strcpy(key_get, "termination_id");
	        if((info = (char *)sym_get(keys, key_get)) == NULL) {
		    test_result(tag, NULL, NULL, "no_Term_Id", keys);
	        } else {
		    if(strcmp(info, "0")) {
		        test_result(tag, NULL, NULL, "fail", keys);
		    } else {
		        test_result(tag, NULL, NULL, "pass", keys);
		    }
		}
	    }
	} else {
	    cuts_report(ctag, keys, info, tag);
	}
    }
    
    /*
     * Extended Format:
     *  - tcid+tc = "!"
     *  - tab separated fields
     *  - no field widths
     *  - fields 6 - ~ are:
     *	start-time (time_t)
     *	duration
     *	termination_id
     *	termination_type
     *	Start Line (of test results in output file)
     *	End Line
     */
    
    if(extended) {

	strcpy(key_get, "termination_id");
	if((ti = (char *)sym_get(keys, key_get)) == NULL) {
	    ti = "No_Termination_ID";
	}

	strcpy(key_get, "termination_type");
	if((tt = (char *)sym_get(keys, key_get)) == NULL) {
	    tt = "No_Termination_Type";
	}

	strcpy(key_get, "duration");
	if((duration = (char *)sym_get(keys, key_get)) == NULL) {
	    duration = "No_Duration";
	}

	strcpy(key_get, "_Start_line");
	if((sl = (char *)sym_get(keys, key_get)) == NULL) {
	    sl = "No_Start_line";
	}

	strcpy(key_get, "_End_line");
	if((el = (char *)sym_get(keys, key_get)) == NULL) {
	    el = "No_End_line";
	}

	strcpy(key_get, "contacts");
	if((contact = (char *)sym_get(keys, key_get)) == NULL) {
	    contact = "No_Contacts";
	}

	strcpy(key_get, "stime");
	if((mystime = (char *)sym_get(keys, key_get)) == NULL) {
	    mystime = "No_stime";
	}

	printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n",
	       tag, "!", "!", is, contact, mystime, duration,
	       ti, tt, sl, el);
    }

    return 0;
}

/*
 *  Print a header made up of the RTS keywords
 *  In "extended" mode, print the header to stderr.
 */
int
print_header(tags)
    SYM tags;
{
    DBT Key, Data;
    char key_get[255];

    FILE *out;

    extern int extended;

    if(extended)
	out = stderr;
    else
	out = stdout;

    fprintf(out, "System Configuration:\n");
    /* build header out of RTS keywords */
    sprintf(key_get, "_RTS");
    Key.data = (void *) key_get;
    if(sym_seq(tags, &Key, &Data, R_CURSOR) == 0) {
	do {
	    if(strcmp((char *)Key.data, "PATH")==0)
		continue;
	    fprintf(out, "%-20.20s %s\n", (char *)Key.data, (char *)Data.data);
	} while(sym_seq(tags, &Key, &Data, R_NEXT)==0);
    }

    fprintf(out, "\n");
    fprintf(out, FORMAT, "tag", "tcid", "testcase", "status", "contact");
    fprintf(out, "-------------------------------------------------------------------------------\n");

    return 0;
}

/*
 * CUTS testcase record
 *
 * This is passed s SYM for the current tag and the initiation keys.
 * The text seen by lex is in yytext (global).
 */
int
cuts_testcase(tag, keys)
    SYM tag, keys;
{
    char *cuts_info[5];
    char key[KEYSIZE];
    char *oldresult, *newresult, *worst_case();
    int tok_num=0;
    extern char yytext[];

    cuts_info[tok_num] = strtok(yytext, "\t ");
    while(tok_num < 5 &&
	  (cuts_info[++tok_num] = strtok(NULL, "\t ")) != NULL )
	;

    strcpy(key, cuts_info[0]);
    strcat(key, ",");
    strcat(key, cuts_info[1]);

#ifdef DEBUGGING
    DEBUG(D_SCAN_CUTS, 1) {
	printf("cuts_testcase: TCID=%s TC=%s Result=%s\n", cuts_info[0],
	       cuts_info[1], cuts_info[2]);
	printf("cuts_testcase: %d %s\n", tok_num, key);
  }
#endif

    if((oldresult=(char *)sym_get(tag, key)) != NULL) {
	/* Duplicate -- assume mulitple runs */
	/* keep "worst case" */
	newresult = worst_case(oldresult, cuts_info[2]);
	sym_put(tag, key, strdup(newresult), PUT_REPLACE);
	free(oldresult);	/* remove the "data" portion of the key */
    } else {
	sym_put(tag, key, strdup(cuts_info[2]), 0);
    }
    return 0;
}

/*
 * Determine a "worst case" status from two given statuses.
 */
static char *
worst_case(t1, t2)
    char *t1, *t2;
{
    /* NULL-terminated table, ordered from worst-case to best-case */
    static char *worst[] = {
	"FAIL",		"BROK",		"PASS",		"CONF",
	"WARN",		"INFO",		NULL,
    };

    char **w1, **w2;

    /* Search the table for each status, then use the index to determine
       which has a lower precedence */
    for(w1=worst; *w1 != NULL && strcmp(t1,*w1); w1++)
	;

    for(w2=worst; *w2 != NULL && strcmp(t2,*w2); w2++)
	;

    if(w1 < w2)
	return(t1);
    else
	return(t2);

}
